/*********************************************************************************
 *      Copyright:  (C) 2022 YanlinZhou<1222>
 *                  All rights reserved.
 *
 *       Filename:  log.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(04/18/2022)
 *         Author:  YanlinZhou <1222>
 *      ChangeLog:  1, Release initial version on "04/18/2022 08:20:38 PM"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <stdarg.h>
#include <string.h>
#include "log.h"

/*
 * Program name variable is provided by the libc
 */
extern const char* __progname;
#define PROGRAM_NAME __progname

/*
 * Logger internal sctructure
 */
typedef struct logger_s {
    FILE    *fp;
    int     loglevel;
    int     use_stdout;   
} logger_t;

logger_t    g_logger;

static const char* LOG_LEVELS[] = { 
    LOG_STRING_ERROR,
    LOG_STRING_WARN,
    LOG_STRING_INFO,
    LOG_STRING_DEBUG 
};

/*
 * initial logger system
 */ 
int logger_init(char *filename, int loglevel)
{
    logger_term();

    g_logger.loglevel = loglevel>LOG_LEVEL_MAX ? LOG_LEVEL_MAX : loglevel;

    /* $filename is NULL or match "stdout"  will use standard output */
    if( !filename || !strcasecmp(filename, "stdout"))
    {
        g_logger.use_stdout = 1;
        g_logger.fp = stderr;
    }
    else
    {
        g_logger.use_stdout = 0;
        g_logger.fp = fopen(filename, "a");
        if( !g_logger.fp ) 
        {
            fprintf(stderr, "Failed to open file '%s': %s", filename, strerror(errno));
            return -1;
        }
    }

    return 0;
}

/*
 * terminate logger system
 */
void logger_term(void)
{
    if( !g_logger.fp )
    {
        return ;
    }

    if( !g_logger.use_stdout ) 
    {
        fclose(g_logger.fp);
    }

    g_logger.use_stdout = 0;
    g_logger.fp = NULL;

    return ;
}


/*
 * Logging functions
 */
int log_generic(const int level, const char* format, va_list args)
{
    char message[256];
    struct tm* current_tm;
    time_t time_now;
    vsprintf(message, format, args);
    time(&time_now);
    current_tm = localtime(&time_now);

    int res = fprintf(g_logger.fp,
            "###: %02i:%02i:%02i [%s] %s"
                , current_tm->tm_hour
                , current_tm->tm_min
                , current_tm->tm_sec
                , LOG_LEVELS[level]
                , message );

    fflush(g_logger.fp);

    if( (check_and_rollback()) < 0)
    {
        printf("check_and_rollback failure!\n");
        return -1;
    }

}

void log_error(char *format, ...)
{
    va_list args;
    va_start(args, format);
    log_generic(LOG_LEVEL_ERROR, format, args);
    va_end(args);
}

void log_warn(char *format, ...)
{
    if (g_logger.loglevel < LOG_LEVEL_WARN) {
        return;
    }

    va_list args;
    va_start(args, format);
    log_generic(LOG_LEVEL_WARN, format, args);
    va_end(args);
}

void log_info(char *format, ...)
{
    if (g_logger.loglevel < LOG_LEVEL_INFO) {
        return;
    }

    va_list args;
    va_start(args, format);
    log_generic(LOG_LEVEL_INFO, format, args);
    va_end(args);
}

void log_debug(char *format, ...)
{
    if (g_logger.loglevel < LOG_LEVEL_DEBUG) {
        return;
    }

    va_list args;
    va_start(args, format);
    log_generic(LOG_LEVEL_DEBUG, format, args);
    va_end(args);
}

/* open my log */
int open_my_log(int debug)
{
	if(debug == 1) //set set log to standard output for debug
	{
		if( logger_init("stdout", LOG_LEVEL_DEBUG) < 0 )
		{
			log_error("initial logger system failure\n");
			return -1;
		}
		log_info("The program running on debug now!\n");
	}

	else //set log to log file 
	{
		if(logger_init(LOG_NAME, LOG_LEVEL_INFO) < 0)
		{
			log_error("initial logger system failure\n");
			return -2;
		}

		log_info("The log is start to record!\n");					
	}

}

int check_and_rollback(void)
{
 
    if (LOG_ROLLBACK_SIZE)
    {
        long curoffset = ftell(g_logger.fp);
//        printf("curoffset:%d\n",curoffset);
 
        if ((curoffset != 0) && (curoffset >= LOG_ROLLBACK_SIZE))
        {
            char cmd[512];
 
            snprintf(cmd, sizeof(cmd), "cp -f %s %s.roll", LOG_NAME, LOG_NAME);
            system(cmd);
            if (-1 == fseek(g_logger.fp, 0L, SEEK_SET))
            {
                fprintf(g_logger.fp, "log rollback fseek failed \n");
                return -2;
            }

            rewind(g_logger.fp);
            truncate(LOG_NAME, 0);
            log_info("Already rollback\n");
            return 0;
        }

        return 0;
    }

    else
    {
//        printf("log no callback!\n");
        return 1;
    }
}